#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "scan.h"
#include "parse.h"

token_t  g_cur_token;
int g_syntax_error = 0;
int g_in_compound = 0;

#define IS_RELOP(x) ( x == TOKEN_EQUAL || x == TOKEN_NOT_EQUAL  || \
                      x == TOKEN_LE || x == TOKEN_GE || \
                      x == TOKEN_LT || x == TOKEN_GT)

static tree_node_t* stmt_list (int is_compound);
static tree_node_t* compound_stmt ();
static tree_node_t* stmt ();
static tree_node_t* if_stmt ();
static tree_node_t* repeat_while_stmt (token_t token);
static tree_node_t* for_stmt ();
static tree_node_t* routine ();
static tree_node_t* fixed_para_stmt (token_t token, int paras);
static tree_node_t* return_stmt ();
static tree_node_t* print_stmt ();
static tree_node_t* new_stmt_node (token_t type);
static tree_node_t* expr ();
static tree_node_t* expr0();
static tree_node_t* expr1();
static tree_node_t* simple_exp ();
static tree_node_t* term ();
static tree_node_t* factor00 ();
static tree_node_t* factor ();
static tree_node_t* factor0 ();
static tree_node_t* real_para_list ();
static tree_node_t* location_para_list ();
static tree_node_t* print_para_list ();
static tree_node_t* id();
static tree_node_t* new_exp_node (exp_type_t exp_type);
static void match (token_t expected);
static void discard ();
static void syntax_error (int lineno);

static void print_space(int space);
static void print_exp_node (tree_node_t* node);
static void print_stmt_node (tree_node_t* node);
static void print_op (token_t op);

void init_tree_node (tree_node_t* node)
{
   int i;
   assert (node);

   node->sibling = NULL;
   for (i = 0; i < MAX_CHILDREN; i++) {
      node->child[i] = NULL;
   }
   node->line_no = get_line_no();
   return;
}

void delete_tree_node (tree_node_t* node)
{
   if ((node->node_type == ExpNode && (node->type.exp == IdExp || node->type.exp == StrExp 
        || node->type.exp == RtnExp) ) ||
       (node->node_type == StmtNode && (node->type.stmt == ROUTINE_STMT ||node->type.stmt == TOKEN_LEARN) )) {
      assert (node->attr.id_str);
      free (node->attr.id_str);
   } 
}

node_handle_t parse ()
{
   tree_node_t* root;
   g_syntax_error = 0;
   g_cur_token = get_token ();
   root = stmt_list (FALSE);
   if (g_syntax_error) {
      g_exec->done();
   }
   return (node_handle_t) root;
}

void delete_tree (tree_node_t* root)
{
   tree_node_t* sb = root->sibling;
   int i;

   for (i = 0; i < MAX_CHILDREN; i++) {
      if (root->child[i]) {
         delete_tree (root->child[i]);
      }
   }
   delete_tree_node (root);
   free (root);

   if (sb) {
       delete_tree (sb);
   }

   return;
}

int check_syntax_error()
{
   return g_syntax_error;
}

static tree_node_t* stmt_list (int is_compound)
{
   tree_node_t* list = NULL;
   tree_node_t* tail = NULL;
   tree_node_t* stmt_node = NULL;
   token_t      end_token = (is_compound ? TOKEN_COMPOUND_END : TOKEN_EOF);

   while (g_cur_token != end_token) {
      if (g_cur_token == TOKEN_EOF) {
         break;
      }
      stmt_node = stmt();
      if (stmt_node == NULL) {
         // blank line 
         continue;
      }
      assert (stmt_node);
      if (list == NULL ) {
         list = tail = stmt_node;
         continue;
      }
      tail->sibling = stmt_node;
      tail = tail->sibling;
   }
   return list; 
}

static tree_node_t* compound_stmt ()
{
   tree_node_t* node = NULL;

   assert (g_in_compound >= 0);
   g_in_compound ++;
   match (TOKEN_COMPOUND_START);
   node = new_stmt_node (COMPOUND_STMT);
   node->child[0] = stmt_list(TRUE);
   match (TOKEN_COMPOUND_END);
   g_in_compound --;
   assert (g_in_compound >= 0);

   return node;
}

static tree_node_t* stmt ()
{
   tree_node_t* node = NULL;
   char* id_str;

   switch (g_cur_token) {
   case TOKEN_RESET:
      node = fixed_para_stmt (TOKEN_RESET, 0);
      break;
   case TOKEN_RETURN:
      node = return_stmt ();
      break;
   case TOKEN_CANVASSIZE:
      node = fixed_para_stmt (TOKEN_CANVASSIZE, 2);
      break;
   case TOKEN_CANVASCOLOR:
      node = fixed_para_stmt (TOKEN_CANVASCOLOR, 3);
      break;
   case TOKEN_COMPOUND_START:
      node = compound_stmt();
      break;
   case TOKEN_IF:
      node = if_stmt();
      break;
   case TOKEN_REPEAT:
      node = repeat_while_stmt(TOKEN_REPEAT);
      break;
   case TOKEN_FOR:
      node = for_stmt();
      break;
   case TOKEN_ID:
      id_str = copy_string (get_token_string());
      match (TOKEN_ID);
      if (g_cur_token == TOKEN_ASSIGN) {
         /* assignment statement */
         match (TOKEN_ASSIGN);
         node = new_stmt_node (TOKEN_ASSIGN);
         node->child[0] = new_exp_node(IdExp);
         node->child[0]->attr.id_str = id_str;
         node->child[1] = expr();
      } else if (g_cur_token == TOKEN_LEFT_BRACKET) {
         /* Routine call statement */
         node = new_stmt_node (ROUTINE_STMT);
         node->attr.id_str = id_str;
         node->child[0] = real_para_list();
      } else {
         discard ();
      }
      break;
   case TOKEN_WHILE:
      node = repeat_while_stmt(TOKEN_WHILE);
      break;
   case TOKEN_LEARN:
      if (!g_in_compound) {
         node = routine ();
      } else {
         discard ();
      }
      break;
   case TOKEN_PENCOLOR:
      node = fixed_para_stmt (TOKEN_PENCOLOR, 3);
      break;
   case TOKEN_PENWIDTH:
      node = fixed_para_stmt (TOKEN_PENWIDTH, 1);
      break;
   case TOKEN_GO:
      node = fixed_para_stmt (TOKEN_GO, 2);
      break;
   case TOKEN_TURNRIGHT:
      node = fixed_para_stmt (TOKEN_TURNRIGHT, 1);
      break;
   case TOKEN_TURNLEFT:
      node = fixed_para_stmt (TOKEN_TURNLEFT, 1);
      break;
   case TOKEN_FORWARD:
      node = fixed_para_stmt (TOKEN_FORWARD, 1);
      break;
   case TOKEN_DIRECTION:
      node = fixed_para_stmt (TOKEN_DIRECTION, 1);
      break;
   case TOKEN_FONTSIZE:
      node = fixed_para_stmt (TOKEN_FONTSIZE, 1);
      break;
   case TOKEN_PRINT:
      node = print_stmt();
      break;
   case TOKEN_HIDE:
      node = fixed_para_stmt (TOKEN_HIDE, 0);
      break;
   case TOKEN_CLEAR:
      node = fixed_para_stmt (TOKEN_CLEAR, 0);
      break;
   case TOKEN_WAIT:
      node = fixed_para_stmt (TOKEN_WAIT, 1);
      break;
   case TOKEN_MESSAGE:
      node = fixed_para_stmt (TOKEN_MESSAGE, 1);
      break;
   case TOKEN_BREAK:
      node = fixed_para_stmt(TOKEN_BREAK, 0);
      break;
   case TOKEN_CONTINUE:
      node = fixed_para_stmt(TOKEN_CONTINUE, 0);
      break;
   case TOKEN_BACKWARD:
      node = fixed_para_stmt (TOKEN_BACKWARD, 1);
      break;
   case TOKEN_CENTER:
      node = fixed_para_stmt (TOKEN_CENTER, 0);
      break;
   case TOKEN_GOX:
      node = fixed_para_stmt (TOKEN_GOX, 1);
      break;
   case TOKEN_GOY:
      node = fixed_para_stmt (TOKEN_GOY, 1);
      break;
   case TOKEN_PENUP:
      node = fixed_para_stmt (TOKEN_PENUP, 0);
      break;
   case TOKEN_PENDOWN:
      node = fixed_para_stmt (TOKEN_PENDOWN, 0);
      break;
   case TOKEN_WRAPON:
      node = fixed_para_stmt (TOKEN_WRAPON, 0);
      break;
   case TOKEN_WRAPOFF:
      node = fixed_para_stmt (TOKEN_WRAPOFF, 0);
      break;
   case TOKEN_SHOW:
      node = fixed_para_stmt (TOKEN_SHOW, 0);
      break;
default:
      discard ();
   }
   return node;
}

static tree_node_t* if_stmt ()
{
   tree_node_t* node = NULL;
   bool compound = false; 

   match (TOKEN_IF);
   node = new_stmt_node (TOKEN_IF);
   node->child[0] = expr();
   if (g_cur_token == TOKEN_COMPOUND_START) {
      node->child[1] = compound_stmt ();
   } else {
      node->child[1] = stmt();
   }

   if (g_cur_token == TOKEN_ELSE) {
      match (TOKEN_ELSE);
      if (g_cur_token == TOKEN_COMPOUND_START) { 
         node->child[2] = compound_stmt ();
      } else {
         node->child[2] = stmt();
      }
   }
   return node;
}

static tree_node_t* repeat_while_stmt (token_t token)
{
   tree_node_t* node = NULL;

   assert (token == TOKEN_REPEAT || token == TOKEN_WHILE);
   match (token);
   node = new_stmt_node (token);
   node->child[0] = expr();
   if (g_cur_token == TOKEN_COMPOUND_START) {
      node->child[1] = compound_stmt ();
   } else {
      node->child[1] = stmt();
   }

   return node;
}

static tree_node_t* for_stmt ()
{
   tree_node_t* node;

   match (TOKEN_FOR);
   node = new_stmt_node(TOKEN_FOR);
   node->child[0] = id();
   match (TOKEN_ASSIGN);
   node->child[1] = expr();
   match (TOKEN_TO);
   node->child[2] = expr();
   if (g_cur_token == TOKEN_COMPOUND_START) {
      node->child[3] = compound_stmt ();
   } else {
      node->child[3] = stmt();
   }
   return node;
}

static tree_node_t* routine ()
{
   tree_node_t* node;

   match (TOKEN_LEARN);
   node = new_stmt_node (TOKEN_LEARN);
   if (g_cur_token != TOKEN_ID) {
      node->attr.id_str = copy_string (UNKNOW_STR);
      return node;
   }
   node->attr.id_str = copy_string (get_token_string ());
   match (TOKEN_ID);

   if (g_cur_token == TOKEN_LEFT_BRACKET) {
      node->child[0] = location_para_list ();
   }
   node->child[1] = compound_stmt();
   return node;
}

static tree_node_t* fixed_para_stmt (token_t token, int paramters)
{
   tree_node_t* stmt = NULL;
   int i;

   stmt = new_stmt_node (token);
   CHECK_MEM (stmt);
   match (token);

   for (i = 0; i < paramters ; i++) {
      assert ( i < MAX_CHILDREN );
      if (i > 0) {
         match (TOKEN_COMMA);
      }
      stmt->child[i] = expr();
   }
   return stmt;
}

static tree_node_t* return_stmt ()
{
   tree_node_t* stmt = NULL;
   stmt = new_stmt_node (TOKEN_RETURN);
   CHECK_MEM (stmt);
   match (TOKEN_RETURN);

   if (g_cur_token == TOKEN_ID || g_cur_token == TOKEN_NUMBER ||
       g_cur_token == TOKEN_STRING || g_cur_token == TOKEN_MINUS ||
       g_cur_token == TOKEN_LEFT_BRACKET) {
      stmt->child[0] = expr();
   }
   return stmt;
}

static tree_node_t* print_stmt ()
{
   tree_node_t* stmt = NULL;

   stmt = new_stmt_node (TOKEN_PRINT);
   CHECK_MEM (stmt);
   match (TOKEN_PRINT);

   stmt->child[0] = print_para_list ();
   return stmt;
}

static tree_node_t* new_stmt_node (token_t type)
{
   tree_node_t* node = (tree_node_t*) malloc (sizeof (tree_node_t));
   CHECK_MEM (node);

   init_tree_node (node);
   node->node_type = StmtNode;
   node->type.stmt = type;
   return node;
}

static tree_node_t* expr()
{
   /* exp --> exp0 "or" exp0 */
   tree_node_t* node = expr0();
   if (g_cur_token != TOKEN_OR) {
      /* so, this is a exp0*/
      return node;
   }
   tree_node_t* p = new_exp_node (OpExp);
   CHECK_MEM (p);

   p->attr.op = g_cur_token;
   match (g_cur_token);

   p->child[0] = node;
   p->child[1] = expr0();
   return p;
}

static tree_node_t* expr0()
{
   /* exp0 --> exp1 "and" exp1 */
   tree_node_t* node = expr1();
   if (g_cur_token != TOKEN_AND) {
      /* so, this is a exp0*/
      return node;
   }
   tree_node_t* p = new_exp_node (OpExp);
   CHECK_MEM (p);

   p->attr.op = g_cur_token;
   match (g_cur_token);

   p->child[0] = node;
   p->child[1] = expr1();
   return p;
}


static tree_node_t* expr1()
{
   /* exp1 --> simple_exp relop simple_exp*/
   tree_node_t* node = simple_exp();

   if (!IS_RELOP (g_cur_token) ) {
      /* so this is a simple_exp*/
      return node;
   }

   tree_node_t* p = new_exp_node (OpExp);
   CHECK_MEM (p);

   p->attr.op = g_cur_token;
   match (g_cur_token);

   p->child[0] = node;
   p->child[1] = simple_exp();
   return p;
}

static tree_node_t* simple_exp ()
{
   /* simple_exp --> simple_exp addop term */
   tree_node_t* node = term ();
   
   while (g_cur_token == TOKEN_PLUS || g_cur_token == TOKEN_MINUS) {
      tree_node_t* p = new_exp_node (OpExp);
      CHECK_MEM (p);
      p->child[0] = node;
      p->attr.op = g_cur_token;
      node = p;
      match (g_cur_token);
      node->child[1] = term();
   }
   return node;
}

static tree_node_t* term ()
{
   /* term --> factor00 mulop term */
   tree_node_t* node = factor00 ();

   while (g_cur_token == TOKEN_MUL || g_cur_token == TOKEN_DIV) {
      tree_node_t* p = new_exp_node (OpExp);
      CHECK_MEM (p);
      p->child[0] = node;
      p->attr.op = g_cur_token;
      node = p;
      match (g_cur_token);
      node->child[1] = factor00();
   }
   return node;
}

static tree_node_t* factor00 ()
{
   /* factor00 --> "not" factor | factor*/
   if (g_cur_token != TOKEN_NOT) {
      return factor();
   }

   tree_node_t* p = new_exp_node (OpExp);
   CHECK_MEM (p);
   p->attr.op = g_cur_token;
   match (g_cur_token);
   p->child[0] = factor();
   return p;
}


static tree_node_t* factor ()
{
   /* factor --> factor0 | - factor0 */
   tree_node_t* t = NULL;

   if (g_cur_token != TOKEN_MINUS) {
      return  factor0();
   }

   match (TOKEN_MINUS);

   t = new_exp_node (OpExp);
   CHECK_MEM (t);
   t->attr.op = TOKEN_MINUS;
   t->child[0] = new_exp_node (ConstExp);
   t->child[0]->attr.val = 0;
   t->child[1] = factor0();
   return t;
}

static tree_node_t* factor0 ()
{
   /* factor0 --> "(" exp ")" | id | num | id "(" real-para-list ")" */
   tree_node_t* t = NULL;
   char* id_str = NULL;

   switch (g_cur_token) {
   case TOKEN_LEFT_BRACKET: 
      match (g_cur_token);
      t = expr ();
      match (TOKEN_RIGHT_BRACKET);
      break;
   case TOKEN_ID:
      id_str = copy_string (get_token_string ());
      match (TOKEN_ID);
      if (g_cur_token == TOKEN_LEFT_BRACKET) {
         /* Routine call */
         t = new_exp_node (RtnExp);
         CHECK_MEM (t);
         t->attr.id_str = id_str;
         t->child[0] = real_para_list();
      } else {
         /* ID */
         t = new_exp_node (IdExp);
         CHECK_MEM (t);
         t->attr.id_str = id_str;
      }
      break;
   case TOKEN_NUMBER:
      t = new_exp_node (ConstExp);
      CHECK_MEM (t);
      sscanf (get_token_string(), "%lf", &(t->attr.val));
      match (TOKEN_NUMBER);
      break;
   case TOKEN_STRING:
      t = new_exp_node (StrExp);
      CHECK_MEM (t);
      t->attr.str_val = copy_string (get_token_string());
      match (TOKEN_STRING);
      break;
   default:
      syntax_error (get_line_no());
      g_cur_token = get_token();
      return NULL;
   }
   return t;
}

static tree_node_t* real_para_list ()
{
   tree_node_t* head = NULL;
   tree_node_t* tail = NULL;

   match (TOKEN_LEFT_BRACKET);
   if (g_cur_token == TOKEN_RIGHT_BRACKET) {
      /* no parameters */  
      match (TOKEN_RIGHT_BRACKET);
      return NULL;
   }
   /* Get the first real parameter */
   head = tail = expr ();
   while (g_cur_token != TOKEN_RIGHT_BRACKET) {
      if (g_cur_token != TOKEN_COMMA) {
         return head;
      }
      match (TOKEN_COMMA);
      tail->sibling = expr ();
      tail = tail->sibling;
   }
   match (TOKEN_RIGHT_BRACKET);

   return head;
}

static tree_node_t* location_para_list ()
{
   tree_node_t* head = NULL;
   tree_node_t* tail = NULL;

   match (TOKEN_LEFT_BRACKET);
   if (g_cur_token == TOKEN_RIGHT_BRACKET) {
      /* no parameters */  
      match (TOKEN_RIGHT_BRACKET);
      return NULL;
   }
   /* Get the first real parameter */
   head = tail = id ();
   while (g_cur_token != TOKEN_RIGHT_BRACKET) {
      if (g_cur_token != TOKEN_COMMA) {
         return head;
      }
      match (TOKEN_COMMA);
      tail->sibling = id ();
      tail = tail->sibling;
   }
   match (TOKEN_RIGHT_BRACKET);

   return head;
}

static tree_node_t* print_para_list ()
{
   tree_node_t* head = NULL;
   tree_node_t* tail = NULL;
   /* Get the first real parameter */
   head = tail = expr ();
   while (g_cur_token == TOKEN_COMMA) {
      if (g_cur_token != TOKEN_COMMA) {
         return head;
      }
      match (TOKEN_COMMA);
      tail->sibling = expr ();
      tail = tail->sibling;
   }

   return head;
}

static tree_node_t* id()
{
   tree_node_t* node = NULL;
   if (g_cur_token == TOKEN_ID) {
      node = new_exp_node (IdExp);
      node->attr.id_str = copy_string (get_token_string());
   }
   match (TOKEN_ID);
   return node;
}

/* ---------------------------------------------- */
static tree_node_t* new_exp_node (exp_type_t exp_type)
{
   tree_node_t* node = (tree_node_t*) malloc (sizeof (tree_node_t));
   CHECK_MEM (node);

   init_tree_node (node);
   node->node_type = ExpNode;
   node->type.exp = exp_type;
   switch (exp_type) {
   case ConstExp:
      node->attr.val = 0;
      break;
   case OpExp:
      node->attr.op = TOKEN_INVALID;
      break;
   case IdExp:
      node->attr.id_str = NULL;
      break;
   case StrExp:
      node->attr.str_val = NULL;
      break;
   case RtnExp:
      node->attr.id_str = NULL;
      break;
   default:
      assert (0);
   }
   return node;
}

static void match (token_t expected)
{
   if (g_cur_token == expected) {
      g_cur_token = get_token ();
      return;
   } 
   syntax_error (get_line_no());
   return;
}

static void discard ()
{
   syntax_error(get_line_no());
   g_cur_token = get_token();
}

static void syntax_error (int lineno)
{
   g_syntax_error = 1;
   char buf[256];
   print_token (buf, 256, g_cur_token, get_token_string());

   print_info (ERROR_INFO, "Syntax error: at line %d, unexpected token: %s.", lineno, buf);
}

char* copy_string (const char* src)
{
   char* str = (char*) malloc (strlen(src)+1);
   CHECK_MEM (str);
   strcpy (str, src);
   return str;
}

void print_parse_tree_node (tree_node_t* node)
{
   assert (node);

   print_info (DEBUG_INFO, "line_no: %d; ", node->line_no);
   if (node->node_type == ExpNode) {
      print_exp_node (node);
   } else if (node->node_type == StmtNode) {
      print_stmt_node (node);
   } else {
      assert (0);
   }
   return;
}

void print_parse_tree (node_handle_t node)
{
   int i;
   static int space = 0; 
   tree_node_t* root = (tree_node_t*)node;
   CHECK_MEM (root);

   print_space (space);
   print_parse_tree_node (root);
   
   space += 3;
   for ( i = 0; i< MAX_CHILDREN; i++) {
      if (((tree_node_t*)root)->child[i]) {
         print_info (DEBUG_INFO, "C%d ", i);
         print_parse_tree ((node_handle_t)(root->child[i]));
      }
   }
   space -= 3;

   if (root->sibling) {
     root = root->sibling;
     print_info (DEBUG_INFO, "S  ");
     print_parse_tree ((node_handle_t)root);
    }

}

static void print_space(int space)
{
   int i; 
   for (i = 0; i < space; i++) {
      print_info(DEBUG_INFO, " ");
   }
}

static void print_exp_node (tree_node_t* node)
{
   assert (node);
   assert (node->node_type == ExpNode);

   print_info (DEBUG_INFO, "Expression node " );
   switch (node->type.exp) {
   case ConstExp:
      print_info (DEBUG_INFO, "constant %lf\r\n", node->attr.val);
      break;
   case OpExp:
      print_info (DEBUG_INFO, "Operation is , ");
      print_op (node->attr.op);
      print_info (DEBUG_INFO, "\r\n");
      break;
   case IdExp:
      print_info (DEBUG_INFO, "ID is %s\r\n", node->attr.id_str);
      break;
   case StrExp:
      print_info (DEBUG_INFO, "String is %s\r\n", node->attr.str_val);
      break;
   case RtnExp:
      print_info (DEBUG_INFO, "Routine call is %s\r\n", node->attr.id_str);
      break;
   default:
      assert(0);
      break;
   }
}


static void print_stmt_node (tree_node_t* node)
{
   assert (node);
   assert (node->node_type == StmtNode);

   print_info (DEBUG_INFO,"Statement Node, ");
   switch (node->type.stmt) {
   case TOKEN_RESET:
      print_info (DEBUG_INFO,"reset\r\n");
      break;
   case TOKEN_RETURN:
      print_info (DEBUG_INFO,"return\r\n");
      break;
   case TOKEN_CANVASSIZE:
      print_info (DEBUG_INFO,"canvassize\r\n");
      break;
   case TOKEN_CANVASCOLOR:
      print_info (DEBUG_INFO,"canvascolo\r\n");
      break;
   case TOKEN_IF:
      print_info (DEBUG_INFO,"if\r\n");
      break;
   case TOKEN_REPEAT:
      print_info (DEBUG_INFO,"repeat\r\n");
      break;
   case TOKEN_ASSIGN:
      print_info (DEBUG_INFO,"ASSIGN\r\n");
      break;
   case TOKEN_WHILE:
      print_info (DEBUG_INFO,"while\r\n");
      break;
   case TOKEN_FOR:
      print_info (DEBUG_INFO,"for\r\n");
      break;
   case ROUTINE_STMT:
      print_info (DEBUG_INFO,"ROUTINE call %s\r\n", node->attr.id_str);
      break;
   case TOKEN_LEARN:
      print_info (DEBUG_INFO,"learn %s\r\n", node->attr.id_str);
      break;
   case TOKEN_PENCOLOR:
      print_info (DEBUG_INFO,"pencolor\r\n");
      break;
   case TOKEN_PENWIDTH:
      print_info (DEBUG_INFO,"penwidth\r\n");
      break;
   case TOKEN_GO:
      print_info (DEBUG_INFO,"go\r\n");
      break;
   case TOKEN_TURNRIGHT:
      print_info (DEBUG_INFO,"turnright\r\n");
      break;
   case TOKEN_TURNLEFT:
      print_info (DEBUG_INFO,"turnleft\r\n");
      break;
   case TOKEN_FORWARD:
      print_info (DEBUG_INFO,"forward\r\n");
      break;
   case TOKEN_DIRECTION:
      print_info (DEBUG_INFO,"direction\r\n");
      break;
   case TOKEN_FONTSIZE:
      print_info (DEBUG_INFO,"fontsize\r\n");
      break;
   case TOKEN_PRINT:
      print_info (DEBUG_INFO,"print\r\n");
      break;
   case TOKEN_HIDE:
      print_info (DEBUG_INFO,"hide\r\n");
      break;
   case TOKEN_CLEAR:
      print_info (DEBUG_INFO,"clear\r\n");
      break;
   case TOKEN_WAIT:
      print_info (DEBUG_INFO,"wait\r\n");
      break;
   case TOKEN_BACKWARD:
      print_info (DEBUG_INFO,"backward\r\n");
      break;
   case COMPOUND_STMT:
      print_info (DEBUG_INFO,"COMPOUND\r\n");
      break;
   case TOKEN_CENTER:
      print_info (DEBUG_INFO,"center\r\n");
      break;
   case TOKEN_GOX:
      print_info (DEBUG_INFO,"gox\r\n");
      break;
   case TOKEN_GOY:
      print_info (DEBUG_INFO,"goy\r\n");
      break;
   case TOKEN_PENUP:
      print_info (DEBUG_INFO,"penup\r\n");
      break;
   case TOKEN_PENDOWN:
      print_info (DEBUG_INFO,"pendown\r\n");
      break;
   case TOKEN_WRAPON:
      print_info (DEBUG_INFO,"wrapon\r\n");
      break;
   case TOKEN_WRAPOFF:
      print_info (DEBUG_INFO,"wrapoff\r\n");
      break;
   case TOKEN_SHOW:
      print_info (DEBUG_INFO,"show\r\n");
      break;
   case TOKEN_MESSAGE:
      print_info (DEBUG_INFO,"message\r\n");
      break;
   case TOKEN_BREAK:
      print_info (DEBUG_INFO, "break\r\n");
      break;
   case TOKEN_CONTINUE:
      print_info (DEBUG_INFO, "continue\r\n");
      break;
   default:
      print_info (ERROR_INFO,"**Unknow statement.**\r\n");
      break;
   }

   return;
}

static void print_op (token_t op)
{
   switch (op) {
   case TOKEN_PLUS:
   case TOKEN_MINUS:
   case TOKEN_MUL:
   case TOKEN_DIV:
   case TOKEN_LT:
   case TOKEN_GT:
      print_info (DEBUG_INFO,"%c", op);
      break;
   case TOKEN_EQUAL:
      print_info (DEBUG_INFO,"==");
      break;
   case TOKEN_NOT_EQUAL:
      print_info (DEBUG_INFO,"!=");
      break;
   case TOKEN_LE :
      print_info (DEBUG_INFO,"<=");
      break;
   case TOKEN_GE :
      print_info (DEBUG_INFO,">=");
      break;
   case TOKEN_AND:
      print_info (DEBUG_INFO, "and");
      break;
   case TOKEN_OR:
      print_info (DEBUG_INFO, "or");
      break;
   case TOKEN_NOT:
      print_info (DEBUG_INFO, "not");
      break;
   default:
      assert (0);
   }
   return;
}


const tree_node_t* get_rightmost_node(const tree_node_t* tree)
{
   assert (tree);

   const tree_node_t* cur = tree;
   const tree_node_t* rightmost;
   while (cur) {
      if (cur->sibling) {
         rightmost = cur;
         cur = cur->sibling;
         continue;
      }
      if (!cur->child[0]) {
         return cur;
      }
      for (int i = MAX_CHILDREN-1; i >= 0; i--) {
         if (cur->child[i]) {
            cur = cur->child[i];
         }
      }
      assert (cur);
   }
   return rightmost;
}
